package utils;

import java.io.*;
import java.util.Vector;

import parser.*;

/**
 * This class contains most of the program variables and
 * all the user defined variables.
 * It also writes and retrieves the config (.ini) file in
 * which the user can set those variables.
 */

public class EDCResource {
    public static final String TEXT_VIEWER = "Text viewer";
    public static final String DTD_VIEWER = "DTD Tree viewer";
    public static final String ANALYZE_FILE = "Analyze DTD file";
    public static final String ANALYZE_FOLDER = "Analyze DTD folder";
    
    public static boolean TEXT_VIEW_REQUIRED = false;
    public static boolean DTD_VIEW_REQUIRED = false;
    public static boolean ANALYZE_FILE_REQUIRED = false;
    public static boolean ANALYZE_FOLDER_REQUIRED = false;
    public static boolean MANAGER_VIEW_REQUIRED = false;
    public static boolean ADD_DTD_REQUIRED = false;
    public static boolean DTD_INFOS_REQUIRED = false;
    
	public static String USER_DIR = System.getProperty("user.dir");
	
	public static String URL_CATALOG_FILE = "urlcatalog.xml";
	public static String URL_CATALOG_PATH = USER_DIR+File.separator+URL_CATALOG_FILE;
	public static File inputFile = new File("dtd");
	public static Logger logger = new Logger("log.xml");
	public static File confFile = new File("config.ini");
	public static boolean fromDTDList = false;
	public static File[] dtds;
	public static boolean toStdOut = true;
	public static boolean verbose = false;
	
	public static DtdFileFilter dtdFilter = new DtdFileFilter();
	public static boolean console = false;
	
	public static boolean checkAny = true;
	public static boolean checkEmpty = true;
	public static boolean checkRedundancy = true;
	public static boolean checkStarredChoiceElement = true;
	public static boolean checkUnStarredChoice = true;
	
	public static String swCheckAll = "All";
	public static String swCheckAny = "Any";
	public static String swCheckEmpty = "Empty";
	public static String swCheckRedundancy = "Redundancy";
	public static String swCheckStarredChoiceElement = "SCElement";
	public static String swUnStarredChoice = "UsChoice";
	
	// Default values
	private static boolean defCheckAny = true;
	private static boolean defCheckEmpty = true;
	private static boolean defCheckRedundancy = true;
	private static boolean defCheckStarredChoiceElement = true;
	private static boolean defCheckUnStarredChoice = true;
	private static boolean defVerbose = false;
	private static File defInputFile = new File("dtd");
	private static String defLogFileName = "log.xml";
	private static File defConfFile = new File("config.ini");
	
	// Sections and attributes
	private static String settingsSect = "Settings";
	private static String inputFileAtt = "input file";
	private static String filtersAtt = "filters";
	private static String loggerAtt = "log";
	private static String confAtt = "config";
	private static String dtdListFileAtt = "read from file";
	private static String verboseAtt = "verbose";
	
	
	public static void checkConfig() {
		resetDefault();
		BufferedReader conf = null;
		if (!confFile.exists()) {
			writeConfig();
		} else try {
			conf = new BufferedReader(new InputStreamReader(new FileInputStream(confFile), "UTF-8"));

			boolean error = false;
			String section = "";
			String attrName = "";
			String attrValue = "";
			
			String line;
			int semiColonIndex;
			int equalIndex;
			while ((line=conf.readLine()) != null) {
				line = line.trim();
				semiColonIndex = line.indexOf(";");
				// The line must not be a commentary or an empty line
				if (semiColonIndex != 0 && !line.equals("")) {
					// restrain the line to the uncommented part
					if (semiColonIndex > 0) {
						line = line.substring(0, semiColonIndex);
					}
					
					if (line.startsWith("[")) {
						section = line.substring(1, line.indexOf("]")).trim().toLowerCase();
					} else if (section.equals(settingsSect.toLowerCase())){
						equalIndex = line.indexOf("=");
						if (equalIndex > 0) {
							attrName = line.substring(0, equalIndex).trim().toLowerCase();
							attrValue = line.substring(equalIndex+1).trim();
							
							if (attrName.equals(inputFileAtt.toLowerCase())) {
								setInput(attrValue);
							} else if (attrName.equals(filtersAtt.toLowerCase())) {
								setFilters(attrValue);
							} else if (attrName.equals(loggerAtt.toLowerCase())) {
								setLogger(attrValue);
							} else if (attrName.equals(confAtt.toLowerCase())) {
								try {
									File otherConf = (new File(attrValue)).getCanonicalFile();
									File thisConf = confFile.getCanonicalFile();
									
									if (otherConf.exists() && otherConf.isFile() && !otherConf.equals(thisConf)) {
										conf.close();
										setConfFile(attrValue);
									}
								} catch (Exception e) {
									error = true;
								}
							} else if (attrName.equals(dtdListFileAtt.toLowerCase())) {
								setDTDListFile(attrValue);
							} else if (attrName.equals(verboseAtt.toLowerCase())) {
								setVerbose((attrValue.equals("yes")?true:false));
							}
						} else { // The config file is not well formed
							error = true;
						}
						
					} else { // The config file is not well formed
						error = true;
					}
				}
			}
			
			conf.close();
			
			if (error) {
				writeConfig();
			}
		} catch (Exception e) { // An error occured, maybe the file is misformed
			if (conf != null) {
				try {
					conf.close();
				} catch (Exception ex){}
			}
			resetDefault();
			writeConfig();
			e.printStackTrace();
		}
	}
	
	public static void writeConfig() {
//		; Do not edit this file while the program is running
//		; This file is stored in UTF-8 encoding
//
//		[Settings]
//		input file=dtd/
//		output file=stdout
//		filters=all
//		log=log.xml
//		verbose=no
//		; other not default settings :
//		; config= ; overrides the configuration settings specified in the default config.ini file
//		; read from file= ; overrides the input file (the file specified in the input file won't be checked)
		try {
			PrintWriter conf = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(confFile), "UTF-8")));
			boolean comment;
			
			conf.println("; Do not edit this file while the program is running");
			conf.println("; This file is stored in UTF-8 encoding");
			conf.println();
			conf.println("["+settingsSect+"]");
			comment = fromDTDList; // comment if the dtd are loading from a DTD list file
			conf.println(((comment)?"; ":"")+inputFileAtt+"="+((comment)?"":inputFile.getPath()));
			conf.println(filtersAtt+"="+getActiveFilters());
			conf.println(loggerAtt+"="+logger.getLogFile().getName());
			conf.println(verboseAtt+"="+((verbose)?"yes":"no"));
			comment = defConfFile.equals(confFile); // comment if the default configuration is used
			conf.println(((comment)?"; ":"")+confAtt+"="+((comment)?"":confFile.getName()));
			comment = !fromDTDList; // comment if the dtd are loading from a directory (or a single DTD)
			conf.println(((comment)?"; ":"")+dtdListFileAtt+"="+((comment)?"":inputFile.getPath()));
			
			conf.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void resetDefault() {
		checkAny = defCheckAny;
		checkEmpty = defCheckEmpty;
		checkRedundancy = defCheckRedundancy;
		checkStarredChoiceElement = defCheckStarredChoiceElement;
		checkUnStarredChoice = defCheckUnStarredChoice;
		verbose = defVerbose;
		inputFile = defInputFile;
		logger.setLogFileName(defLogFileName);
		confFile = new File(defConfFile.getName());
		try {
			setInput(inputFile.getCanonicalPath());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static String getActiveFilters() {
		String f = "";
		int count = 0;
		if (checkAny) {
			f+=swCheckAny+" ";
			count++;
		}
		if (checkEmpty) {
			f+=swCheckEmpty+" ";
			count++;
		}
		if (checkRedundancy) {
			f+=swCheckRedundancy+" ";
			count++;
		}
		if (checkStarredChoiceElement) {
			f+=swCheckStarredChoiceElement+" ";
			count++;
		}
		if (checkUnStarredChoice) {
			f+=swUnStarredChoice;
			count++;
		}
		
		if (count == 5) {
			f = swCheckAll;
		} else if (count == 0) {
			f = "N"+swCheckAll;
		} else {
			f = f.trim();
		}
		
		return f;
	}
	
	public static void setInput(String input) throws Exception {
		if (!fromDTDList) {
			inputFile = new File(input);
			if (inputFile.isFile()) {
				dtds = new File[]{inputFile};
			} else if (inputFile.isDirectory()) {
				dtds = inputFile.listFiles(EDCResource.dtdFilter);
			} else {
				throw new Exception("This file cannot be handled !");
			}
		}
	}
	
	public static void setFilters(String filtersString) {
		// All Any Empty Redundancy SCElement UsChoice
		String[] filters = filtersString.split(" ");
		String filter;
		Vector<String> negativeElems = new Vector<String>();
		Vector<String> positiveElems = new Vector<String>();
		for (int i=0; i<filters.length; i++) {
			filter = filters[i].toLowerCase();
			if (filter.startsWith("n")) {
				negativeElems.add(filter.substring(1));
			} else {
				positiveElems.add(filter);
			}
		}
		
		// if there is at least one "positive" element, that mean it is "exclusive", we set all other elements to false
		if (positiveElems.size() > 0) {
			checkAny = false;
			checkEmpty = false;
			checkRedundancy = false;
			checkStarredChoiceElement = false;
			checkUnStarredChoice = false;
		}
		
		// It is important to set the exclusive elements first because of the "logical" meaning :
		// if the user set both one element and its contrary, the element should not be checked (eg. -f any Nany : priority goes to negation)
		Vector<String> elem;
		for (int i=0; i<2; i++) {
			if (i==0)
				elem = positiveElems;
			else
				elem = negativeElems;
			for (String f : elem) {
				if (f.equals(swCheckAll.toLowerCase())) {
					checkAny = (i==1)?false:true;
					checkEmpty = (i==1)?false:true;
					checkRedundancy = (i==1)?false:true;
					checkStarredChoiceElement = (i==1)?false:true;
					checkUnStarredChoice = (i==1)?false:true;
				} else if (f.equals(swCheckAny.toLowerCase())) {
					checkAny = (i==1)?false:true;
				} else if (f.equals(swCheckEmpty.toLowerCase())) {
					checkEmpty = (i==1)?false:true;
				} else if (f.equals(swCheckRedundancy.toLowerCase())) {
					checkRedundancy = (i==1)?false:true;
				} else if (f.equals(swCheckStarredChoiceElement.toLowerCase())) {
					checkStarredChoiceElement = (i==1)?false:true;
				} else if (f.equals(swUnStarredChoice.toLowerCase())) {
					checkUnStarredChoice = (i==1)?false:true;
				}
			}
		}
	}
	
	public static void setLogger(String log) {
		logger.setLogFileName(log);
	}
	
	public static void setConfFile(String conf) {
		confFile = new File(conf);
		checkConfig();
	}
	
	public static void setDTDListFile(String input) throws Exception {
		fromDTDList = true;
		inputFile = new File(input);
		
		BufferedReader f = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile)));
		String line;
		File d;
		Vector<File> dtdList = new Vector<File>();
		while ((line=f.readLine()) != null) {
			try {
				d = new File(line.trim());
				
				if (!d.exists())
					d = new File(inputFile.getParentFile(), d.getName());
				
				if (d.isFile()) {
					dtdList.add(d);
				} else if (d.isDirectory()) {
					File[] list = d.listFiles(EDCResource.dtdFilter);
					for (int i=0; i<list.length; i++) {
						dtdList.add(list[i]);
					}
				} else {
					throw new Exception();
				}
			} catch (Exception e) {
				logger.addNewFileEntry(line.trim());
				logger.addException("The file doesn't exist or is a URL");
				logger.closeFileEntry();
			}
		}
		dtds = dtdList.toArray(new File[dtdList.size()]);
	}
	
	public static void setVerbose(boolean val) {
		verbose = val;
	}
}
